/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  iterator.c
 *        \brief  This is a general-purpose C language iterator module
 *       \author  Lamdonn
 *        \email  Lamdonn@163.com
 *      \version  1.0.0
 *      \license  GPL2
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#include "vlib.h"
#include "stdlib.h"
#include <stdio.h>

/* The use of iterators depends on the iterator methods provided by various containers */
/* str */
extern void* str_iterator_begin(vobject_t object);
extern void* str_iterator_end(vobject_t object);
extern void* str_iterator_move(vobject_t object, void *node, int step);
extern void str_iterator_nfs(void *node, void **first, void **second);
/* vector */
extern void* vector_iterator_begin(vobject_t object);
extern void* vector_iterator_end(vobject_t object);
extern void* vector_iterator_move(vobject_t object, void *node, int step);
extern void vector_iterator_nfs(void *node, void **first, void **second);
/* deque */
extern void* deque_iterator_begin(vobject_t object);
extern void* deque_iterator_end(vobject_t object);
extern void* deque_iterator_move(vobject_t object, void *node, int step);
extern void deque_iterator_nfs(void *node, void **first, void **second);
/* list */
extern void* list_iterator_begin(vobject_t object);
extern void* list_iterator_end(vobject_t object);
extern void* list_iterator_move(vobject_t object, void *node, int step);
extern void list_iterator_nfs(void *node, void **first, void **second);
/* set */
extern void* set_iterator_begin(vobject_t object);
extern void* set_iterator_end(vobject_t object);
extern void* set_iterator_move(vobject_t object, void *node, int step);
extern void set_iterator_nfs(void *node, void **first, void **second);
/* map */
extern void* map_iterator_begin(vobject_t object);
extern void* map_iterator_end(vobject_t object);
extern void* map_iterator_move(vobject_t object, void *node, int step);
extern void map_iterator_nfs(void *node, void **first, void **second);

/* iterator ops define */
typedef struct
{
    void *(*begin)(vobject_t object);                           /* get the container begin iteration location */
    void *(*end)(vobject_t object);                             /* get the container end iteration location */
    void *(*move)(vobject_t object, void *node, int step);      /* container iterator moves */
    void (*nfs)(void *node, void **first, void **second);       /* get information about the first and second members of the iterator container */
} iterator_ops;

/* The set of operation functions associated with vlib container iterators */
static iterator_ops ops[9] = {
/*  begin                   end                     move                    nfs                     */
    {NULL,                  NULL,                   NULL,                   NULL                    }, /* unknow */
    {str_iterator_begin,    str_iterator_end,       str_iterator_move,      str_iterator_nfs        }, /* str */
    {vector_iterator_begin, vector_iterator_end,    vector_iterator_move,   vector_iterator_nfs     }, /* vector */
    {deque_iterator_begin,  deque_iterator_end,     deque_iterator_move,    deque_iterator_nfs      }, /* queue */
    {deque_iterator_begin,  deque_iterator_end,     deque_iterator_move,    deque_iterator_nfs      }, /* stack */
    {deque_iterator_begin,  deque_iterator_end,     deque_iterator_move,    deque_iterator_nfs      }, /* deque */
    {list_iterator_begin,   list_iterator_end,      list_iterator_move,     list_iterator_nfs       }, /* list */
    {set_iterator_begin,    set_iterator_end,       set_iterator_move,      set_iterator_nfs        }, /* set */
    {map_iterator_begin,    map_iterator_end,       map_iterator_move,      map_iterator_nfs        }, /* map */
};

/* vlib container type identification */
static int types[9] = {1, 0, 2, 3, 4, 5, 6, 7, 8}; /* ident transtype index */
const char *vtype_table[9] = {
    "unknow",   /* ident 0 */
    "str",      /* ident -1 */
    "vector",   /* ident 1 */
    "queue",    /* ident 2 */
    "stack",    /* ident 3 */
    "deque",    /* ident 4 */
    "list",     /* ident 5 */
    "set",      /* ident 6 */
    "map",      /* ident 7 */
};

int vtype(vobject_t object)
{
    /* check whether the object is valid */
    if (!object) return VTYPE_UNKNOW;

    /* +1 becomes the index of the type table */
    if (object->ident >= -1 && object->ident <= 7) return types[object->ident + 1];

    return VTYPE_UNKNOW;
}

iterator iterator_create(vobject_t object, int pos)
{
    iterator it = {object, NULL, 0};
    int type = vtype(object);

    /* check whether the type is valid */
    if (type == VTYPE_UNKNOW) return it;

    /* the iterator is invalid when the container is empty */
    if (it.object->size == 0)
    {
        if (pos == 0) return it;
        it.pos = -1;
        return it;
    }

    /* pos is out of the valid range, the default is end */
    if (pos < 0 || pos >= it.object->size) pos = it.object->size - 1; /* for end */

    /* get the node with a shorter number of moves */
    if (pos < (it.object->size >> 1))
    {
        it.node = ops[type].begin(it.object);
        it.pos = 0;
    }
    else
    {
        it.node = ops[type].end(it.object);
        it.pos = it.object->size - 1;
    }

    /* iterator moves to the initialization position */
    iterator_move(&it, pos - it.pos);

    return it;
}

int iterator_valid(iterator *it)
{
    /* check whether the address of iterator is valid */
    if (!it) return 0;
    return (it->object && it->node && it->pos >= 0 && it->pos < it->object->size) ? 1 : 0;
}

int iterator_move(iterator *it, int step)
{
    int type;

    /* check whether the address of iterator is valid */
    if (!it) return 0;

    /* move 0 step and return directly */
    if (step == 0) return 0;

    /* check whether the type is valid */
    type = vtype(it->object);
    if (type == VTYPE_UNKNOW) return 0;

    /* calculate whether the number of moves is valid */
    if (step + it->pos < 0 || step + it->pos >= it->object->size) goto FAIL;

    /* call the ops move function to move */
    it->node = ops[type].move(it->object, it->node, step);
    it->pos += step;

    return step;

FAIL:
    it->pos = step > 0 ? it->object->size : -1; /* set to an invalid location */
    it->node = NULL;
    return 0;
}

void* iterator_key(iterator *it)
{
    void *key;
    int type;

    /* check whether the address of iterator is valid */
    if (!it) return NULL;

    /* check whether the type is valid */
    type = vtype(it->object);
    if (type == VTYPE_UNKNOW) return NULL;

    /* get the iterator member */
    ops[type].nfs(it->node, &key, NULL);
    if (!key) key = &it->pos; /* the default key is position */

    return key;
}

void* iterator_value(iterator *it)
{
    void *value;
    int type;

    /* check whether the address of iterator is valid */
    if (!it) return NULL;

    /* check whether the type is valid */
    type = vtype(it->object);
    if (type == VTYPE_UNKNOW) return NULL;

    /* get the iterator member */
    ops[type].nfs(it->node, NULL, &value);

    return value;
}
